#pragma once

#include <iostream>
#include <pthread.h>
#include <vector>
#include <semaphore.h>

const int gdefaultcap = 5;
template <class T>
class SurrondQueue
{

private:
    void P(sem_t &sem)
    {
        sem_wait(&sem);
    }
    void V(sem_t &sem)
    {
        sem_post(&sem);
    }
    void Lock(pthread_mutex_t &mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    void UnLock(pthread_mutex_t &mutex)
    {
        pthread_mutex_unlock(&mutex);
    }

public:
    SurrondQueue(int max_cap = gdefaultcap)
        : _max_cap(max_cap), _suque(max_cap, T()), _pos_push(0), _pos_pop(0)
    {

        sem_init(&_consume_sem, 0, 0);
        sem_init(&_product_sem, 0, _max_cap);
        pthread_mutex_init(&_consume_mutex, nullptr);
        pthread_mutex_init(&_product_mutex, nullptr);
    }
    ~SurrondQueue()
    {
        sem_destroy(&_consume_sem);
        sem_destroy(&_product_sem);
        pthread_mutex_destroy(&_consume_mutex);
        pthread_mutex_destroy(&_product_mutex);
    }

    void push(const T &in)
    {
        P(_product_sem);
        Lock(_product_mutex);
        _suque[_pos_push] = in;
        _pos_push++;
        _pos_push %= _max_cap;
        UnLock(_product_mutex);
        V(_consume_sem);
    }

    void pop(T *const out)
    {
        P(_consume_sem);
        Lock(_consume_mutex);
        *out = _suque[_pos_pop];
        _pos_pop++;
        _pos_pop %= _max_cap;
        UnLock(_consume_mutex);
        V(_product_sem);
    }

private:
    std::vector<T> _suque;
    int _pos_push;
    int _pos_pop;
    int _max_cap;

    sem_t _consume_sem;
    sem_t _product_sem;

    pthread_mutex_t _consume_mutex;
    pthread_mutex_t _product_mutex;
};